# coding=utf-8

import sys

from app.cores.variable import Variable
from app.cores.exceptions import PreScriptExecException, PostScriptExecException
from app.cores.case.base.last_result import LastResult


def exec_script(source, project_id, log, script_type, glb=None, loc=None):
    """
    执行py脚本
    :param source: py脚本内容
    :type source: str
    :param project_id: 当前执行案例所属的项目id，以获取项目级变量
    :type project_id: int
    :param log: 日志对象
    :type log: Logger
    :param script_type: 脚本类型
    :type script_type: str
    :param glb: 全局变量
    :param loc: 局部变量
    :return:
    """
    vars = Variable.get_project_variable(project_id=project_id)  # TODO 直接从session里面获取project_id??
    if glb is None:
        glb = {}
    if loc is None:
        loc = {}
    loc['vars'] = vars
    loc['log'] = log
    loc['__name__'] = '__main__'
    try:
        exec(source, glb, loc)
    except Exception as e:
        if isinstance(e, SyntaxError):
            lineno = e.lineno
        else:
            tb = sys.exc_info()[-1]
            while tb.tb_next:
                tb = tb.tb_next
            lineno = tb.tb_lineno
        if script_type == 'PREPROCESSOR_SCRIPT':
            failure_message = '预处理脚本执行异常: m行号: %s, 错误信息: %s, 错误类型: %s' % (lineno, e.args[0], type(e))
            script_exception = PreScriptExecException(lineno, e.args[0], failure_message)
            log.error('预处理脚本执行异常: 行号: %s, 错误信息: %s' % (script_exception.line_no, script_exception.value))
            raise script_exception
        elif script_type == 'POSTPROCESSOR_SCRIPT':
            loc['failure'] = True  # 后处理脚本异常时，将断言结果置为失败
            failure_message = '后处理脚本执行异常: 行号: %s, 错误信息: %s, 错误类型: %s' % (lineno, e.args[0], type(e))
            script_exception = PostScriptExecException(lineno, e.args[0], failure_message)
            loc['failure_message'] = failure_message
            log.error(failure_message)
            raise script_exception
        else:
            raise e
    return glb, loc


def exec_preprocessor_script(source, project_id, log):
    """
    执行预处理脚本
    :param source: py脚本内容
    :type source: str
    :param project_id: 当前执行案例所属的项目id，以获取项目级变量
    :type project_id: int
    :param log: 日志对象
    :type log: Logger
    """
    loc = {}
    last_result = LastResult.get_last_result()
    if last_result is None:
        last_result = {}
    loc['result'] = last_result.get('result')
    loc['request_headers'] = last_result.get('request_headers')
    loc['request_body'] = last_result.get('request_body')
    loc['response_headers'] = last_result.get('response_headers')
    loc['response_body'] = last_result.get('response_body')
    exec_script(source=source, project_id=project_id, log=log, script_type='PREPROCESSOR_SCRIPT', loc=loc)


def exec_postprocessor_script(source, project_id, case, log):
    """
    执行后处理脚本
    :param source: py脚本内容
    :type source: str
    :param project_id: 当前执行案例所属的项目id，以获取项目级变量
    :type project_id: int
    :param case: 案例对象
    :type case: Case
    :param log: 日志对象
    :type log: Logger
    :return: 断言成功或失败
    :rtype: bool
    """
    loc = {}
    last_result = LastResult.get_last_result()
    if last_result is None:
        last_result = {}
    loc['result'] = last_result.get('result')
    loc['request_headers'] = last_result.get('request_headers')
    loc['request_body'] = last_result.get('request_body')
    loc['response_headers'] = last_result.get('response_headers')
    loc['response_body'] = last_result.get('response_body')
    loc['failure'] = False  # 后处理断言失败标志
    loc['failure_message'] = ''  # 后处理失败信息
    glb, loc = exec_script(source=source, project_id=project_id, log=log, script_type='POSTPROCESSOR_SCRIPT', glb={},
                           loc=loc)
    if loc.get('failure') is None:
        postprocessor_failure = False
    else:
        postprocessor_failure = bool(loc.get('failure'))
    if loc.get('failure_message') is None:
        postprocessor_failure_message = ''
    else:
        postprocessor_failure_message = str(loc.get('failure_message'))
    case.specific_case.update_postprocessor_failure(postprocessor_failure=postprocessor_failure,
                                                    postprocessor_failure_message=postprocessor_failure_message)
    return postprocessor_failure, postprocessor_failure_message